package com.example.springbootdemo.aop;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@Slf4j
public class CglibProxy extends Target{
  public static void main(String[] args) {
    CglibProxy cglibProxy = new CglibProxy();
    Target target = new Target();
    cglibProxy.setMethodInterceptor((obj, method, args1, proxy) -> {
      log.info("before");
      return method.invoke(target, args1);
    });
    cglibProxy.foo();
  }

  private MethodInterceptor methodInterceptor;

  public void setMethodInterceptor(MethodInterceptor methodInterceptor){
    this.methodInterceptor = methodInterceptor;
  }
  static Method foo;
  static MethodProxy fooProxy;
  static {
    try {
      foo = Target.class.getMethod("foo");
      fooProxy = MethodProxy.create(Target.class, CglibProxy.class,"()V","foo","fooSuper");
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    }
  }
  @Override
  public void foo() {
    try {
      methodInterceptor.intercept(this,foo,new Object[0],fooProxy);
      // 调用方法从这里进入，根据callback实现决定是反射调用还是proxy调用
    } catch (Throwable e) {
      throw new RuntimeException(e);
    }
  }

  public void fooSuper(){
    super.foo();
  }
}

class TargetFastClass{

  static Signature foo = new Signature("foo","()V");
  //获取目标方法编号
  public int getIndex(Signature sig){
    if (foo.equals(sig)){
      return 0;
    }
   return 1;
  }

  // 根据编号，调用目标方法
  public Object invoke(int index, Object obj, Object[] args) throws InvocationTargetException{
    if (index == 0){
      Target target = (Target) obj;
      target.foo();
      return null;
    }
    return null;
  }

  public static void main(String[] args) throws InvocationTargetException {
    TargetFastClass fastClass = new TargetFastClass();
    int foo1 = fastClass.getIndex(new Signature("foo", "()V"));
    System.out.println(foo1);
    fastClass.invoke(foo1,new Target(),new Object[0]);
  }

}
